/**
 *  @file: MainTask.cpp
 *  @author: nwz
 *  @brief: 
 *  @date: 2024��7��25��
 * 
 */

#include <code/task/MainTask.h>
#define TAG "main"
#include "all_header.h"
#include <code/ui/ui.h>

extern UART_HandleTypeDef huart1;
#define ABORT() ASSERT(0)

namespace task {
/// bsp
static auto lcd = bsp::LCD();
static auto backlight = bsp::Backlight();
static auto bkram = bsp::BKPSRam();
/// tasks
static auto mainTask = task::MainTask();
static auto &consoleTask = ConsoleTask::getInstance();
static auto dataTask = task::DataTask();
static auto ledTask = task::LedTask();
static auto lcdTask = task::LcdTask(lcd, backlight);
static auto userKeyTask = task::UserKeyTask(lcdTask);
static auto sleepTask = task::SleepTask(bkram);
static auto lvglTask = task::LvglTask(lcd, backlight);
static auto uiTask = task::UiTask(lcdTask);
static auto debugTask = task::DebugTask(consoleTask, dataTask, ledTask,
		userKeyTask, lcdTask, sleepTask, lvglTask, uiTask);

extern "C" {
void lcd_dma_send_callback(void) {
	lcd.sendDMACallback();
}
void lcd_transmit(int32_t x1, int32_t y1, int32_t x2, int32_t y2,
		const uint8_t *data) {
	uint32_t size = (x2 - x1 + 1) * (y2 - y1 + 1) * 2;
	lcd.sendDMA(x1, y1, x2, y2, data, size);
}

void Page_Load(Page_t *page) {

}
void Page_Back() {

}
} // extern C

MainTask::MainTask() {
	// TODO Auto-generated constructor stub
}

MainTask::~MainTask() {
	// TODO Auto-generated destructor stub
}
void MainTask::run(MainTask *task) {
	task->init();
	task->run_();
}

void MainTask::run_() {

	// bsp init
	bkram.init();
	// need check first
	SleepTask::CheckOk(bkram);
	backlight.init(0);

	// task init
	consoleTask.init();
	LOG_D("version %d.%d %s %s" NL, VERSION, VERSION_SUB, __DATE__, __TIME__);
	dataTask.init();
	ledTask.init();
	userKeyTask.init();
	lcdTask.init(dataTask.getBacklight());
	lvglTask.init();

	if (xTaskCreate((void (*)(void*)) ConsoleTask::writeLoop, "con_write",
	CONSOLE_WRITE_TASK_STACK_SIZE, &consoleTask,
	CONSOLE_WRITE_TASK_PRIORITY, nullptr) != pdPASS) {
		ABORT();
	}

	if (xTaskCreate((void (*)(void*)) ConsoleTask::readLoop, "con_read",
	CONSOLE_READ_TASK_STACK_SIZE, &consoleTask,
	CONSOLE_READ_TASK_PRIORITY, nullptr) != pdPASS) {
		ABORT();
	}

	if (xTaskCreate((void (*)(void*)) DataTask::run, "data",
	DATA_TASK_STACK_SIZE, &dataTask, DATA_TASK_PRIORITY, nullptr) != pdTRUE) {
		ABORT();
	}

	if (xTaskCreate((void (*)(void*)) LedTask::run, "led",
	LED_TASK_STACK_SIZE, &ledTask, LED_TASK_PRIORITY, nullptr) != pdTRUE) {
		ABORT();
	}

	if (xTaskCreate((void (*)(void*)) UserKeyTask::run, "user_key",
	USER_KEY_TASK_STACK_SIZE, &userKeyTask, USER_KEY_TASK_PRIORITY,
			nullptr) != pdTRUE) {
		ABORT();
	}

	if (xTaskCreate((void (*)(void*)) SleepTask::run, "sleep",
	SLEEP_TASK_STACK_SIZE, &sleepTask, SLEEP_TASK_PRIORITY, nullptr) != pdTRUE) {
		ABORT();
	}

	if (xTaskCreate((void (*)(void*)) DebugTask::run, "debug",
	DEBUG_TASK_STACK_SIZE, &debugTask, DEBUG_TASK_PRIORITY, nullptr) != pdTRUE) {
		ABORT();
	}

	lcd.init(); // must init before thread run
	if (xTaskCreate((void (*)(void*)) LcdTask::run, "lcd",
	LCD_TASK_STACK_SIZE, &lcdTask, LCD_TASK_PRIORITY, nullptr) != pdTRUE) {
		ABORT();
	}

	if (xTaskCreate((void (*)(void*)) LvglTask::run, "lvgl",
	LVGL_TASK_STACK_SIZE, &lvglTask, LVGL_TASK_PRIORITY, nullptr) != pdTRUE) {
		ABORT();
	}

	if (xTaskCreate((void (*)(void*)) UiTask::run, "ui",
	UI_TASK_STACK_SIZE, &uiTask, UI_TASK_PRIORITY, nullptr) != pdTRUE) {
		ABORT();
	}

	ledTask.setState(LedTask::led_state_id::WORK);

	Event event;
	while (true) {
		ASSERT(xQueueReceive(queue,&event,portMAX_DELAY) == pdTRUE);
		switch (event) {
		case Event::UPDATE_INPUT_TIME:
			updateInputTime();
			break;
		case Event::KEY:
			processKey();
			break;
		case Event::UI_PAGE_CHANGE:
			processPageChanged();
			break;
		}
	}
}

void MainTask::start() {
	ASSERT(
			xTaskCreate((void(*)(void*))MainTask::run,"main",MAIN_TASK_STACK_SIZE,&mainTask,MAIN_TASK_PRIORITY,nullptr) == pdPASS);
}

int MainTask::init() {
	queue = xQueueCreate(10, sizeof(Event));
	ASSERT(queue);
	return 0;
}

void MainTask::processPageChanged() {
	uiTask.pubPageChange();
}

void MainTask::updateInputTime() {
	latest_input_time = NOW();
	// notify lcd
	lcdTask.updateTime();
}

void MainTask::processKey() {
	UserKeyTask::notify_event_t evt;
	auto ret = userKeyTask.read(&evt);
	ASSERT(ret == 0);
	LOG_D("read key %d %s" NL, evt.id,
			evt.event == UserKeyTask::Event::CLICK ? "clicks" : "long_click");

	if (evt.id == UserKeyTask::Key::ON_OFF) {
		if (evt.event == UserKeyTask::Event::LONG_CLICK) {
			sleepTask.enterSleep();
			return;
		} else if (evt.event == UserKeyTask::Event::CLICK) {
			if (!lcdTask.isShutdown()) {
				lcdTask.shutdown();
				return;
			}
		}
	}

	uiTask.pubKeyEvent(&evt);
}

void MainTask::pub(Event evt) {
	ASSERT(xQueueSend(queue,&evt,DEFAULT_WAIT_TICKS) == pdTRUE);
}

void MainTask::notifyUserInputTime() {
	mainTask.pub(Event::UPDATE_INPUT_TIME);
}

void MainTask::notifyUiPageChange() {
	mainTask.pub(Event::UI_PAGE_CHANGE);
}

void MainTask::nofityKeyEvent() {
	mainTask.pub(Event::KEY);
}

} /* namespace bsp */
